package online.heycm.rbac.domain.basic.repository;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import online.heycm.platform.common.entity.exception.Assert;
import online.heycm.platform.common.entity.result.Optional;
import online.heycm.rbac.common.cache.ParamCfgCache;
import online.heycm.rbac.common.cache.RoleCache;
import online.heycm.rbac.common.constant.ErrorCode;
import online.heycm.rbac.common.constant.ParamCfg;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacInterface;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacResource;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRole;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRoleInterface;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRoleResource;
import online.heycm.rbac.domain.basic.repository.mapper.BasicRbacInterfaceMapper;
import online.heycm.rbac.domain.basic.repository.mapper.BasicRbacResourceMapper;
import online.heycm.rbac.domain.basic.repository.mapper.BasicRbacRoleInterfaceMapper;
import online.heycm.rbac.domain.basic.repository.mapper.BasicRbacRoleMapper;
import online.heycm.rbac.domain.basic.repository.mapper.BasicRbacRoleResourceMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author heycm
 * @since 2024/1/14 15:27
 */
@Component
public class RbacRepository {

    @Autowired
    private BasicRbacRoleMapper basicRbacRoleMapper;

    @Autowired
    private BasicRbacInterfaceMapper basicRbacInterfaceMapper;

    @Autowired
    private BasicRbacRoleInterfaceMapper basicRbacRoleInterfaceMapper;

    @Autowired
    private BasicRbacResourceMapper basicRbacResourceMapper;

    @Autowired
    private BasicRbacRoleResourceMapper basicRbacRoleResourceMapper;

    @Autowired
    private ParamCfgCache paramCfgCache;

    public List<BasicRbacRole> allRoles() {
        return basicRbacRoleMapper.selectList(null);
    }

    public List<BasicRbacInterface> allInterfaces() {
        return basicRbacInterfaceMapper.selectList(null);
    }

    public List<BasicRbacRoleInterface> allRoleInterfaces() {
        return basicRbacRoleInterfaceMapper.selectList(null);
    }

    public List<BasicRbacResource> roleResources(int[] roleIds) {
        return roleResources(Arrays.stream(roleIds).boxed().collect(Collectors.toSet()));
    }

    public List<BasicRbacResource> roleResources(Set<Integer> roleIds) {
        return basicRbacResourceMapper.selectRoleResources(roleIds);
    }

    public Optional save(BasicRbacRole role) {
        if (ifRoleExist(role.getRoleEn())) {
            return Optional.error(ErrorCode.ROLE_REPEAT);
        }
        role.setSts(1);
        boolean ok = basicRbacRoleMapper.insert(role) > 0;
        return Optional.ifFail(ok, ErrorCode.ROLE_INSERT);
    }

    private boolean ifRoleExist(String roleCode) {
        LambdaQueryWrapper<BasicRbacRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicRbacRole::getRoleEn, roleCode);
        BasicRbacRole role = basicRbacRoleMapper.selectOne(wrapper);
        return role != null;
    }

    public boolean updateById(BasicRbacRole role) {
        String baseRole = paramCfgCache.getCache(ParamCfg.BASE_ROLE);
        BasicRbacRole roleCache = RoleCache.getById(role.getRoleId());
        Assert.isTrue(!baseRole.equals(roleCache.getRoleEn()), ErrorCode.BASE_ROLE_UPDATE);
        return basicRbacRoleMapper.updateById(role) > 0;
    }

    public BasicRbacRole getRoleById(Integer roleId) {
        return basicRbacRoleMapper.selectById(roleId);
    }

    public boolean save(BasicRbacInterface rbacInterface) {
        return basicRbacInterfaceMapper.insert(rbacInterface) > 0;
    }

    public boolean updateById(BasicRbacInterface rbacInterface) {
        return basicRbacInterfaceMapper.updateById(rbacInterface) > 0;
    }

    public List<BasicRbacInterface> listInterface(Wrapper<BasicRbacInterface> wrapper) {
        return basicRbacInterfaceMapper.selectList(wrapper);
    }

    public void pageInterface(IPage<BasicRbacInterface> page, Wrapper<BasicRbacInterface> queryWrapper) {
        basicRbacInterfaceMapper.selectPage(page, queryWrapper);
    }

    public List<Integer> listRoleResources(Integer roleId) {
        LambdaQueryWrapper<BasicRbacRoleInterface> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicRbacRoleInterface::getRoleId, roleId);
        List<BasicRbacRoleInterface> roleInterfaces = basicRbacRoleInterfaceMapper.selectList(wrapper);
        return roleInterfaces.stream().map(BasicRbacRoleInterface::getInterfaceId).collect(Collectors.toList());
    }

    @Transactional
    public void saveBatchRoleInterface(List<BasicRbacRoleInterface> rbacRoleInterfaces) {
        Date now = new Date();
        rbacRoleInterfaces.forEach(r -> r.setCreatedAt(now));
        LambdaQueryWrapper<BasicRbacRoleInterface> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicRbacRoleInterface::getRoleId, rbacRoleInterfaces.get(0).getRoleId());
        basicRbacRoleInterfaceMapper.delete(wrapper);
        int row = basicRbacRoleInterfaceMapper.insertBatch(rbacRoleInterfaces);
        Assert.isTrue(rbacRoleInterfaces.size() == row, ErrorCode.ROLE_INTERFACE_INSERT);
    }

    public boolean save(BasicRbacResource resource) {
        return basicRbacResourceMapper.insert(resource) > 0;
    }

    public boolean updateById(BasicRbacResource resource) {
        return basicRbacResourceMapper.updateById(resource) > 0;
    }

    public List<BasicRbacResource> listResource(Wrapper<BasicRbacResource> wrapper) {
        return basicRbacResourceMapper.selectList(wrapper);
    }

    public void pageResource(IPage<BasicRbacResource> page, Wrapper<BasicRbacResource> wrapper) {
        basicRbacResourceMapper.selectPage(page, wrapper);
    }

    public List<Integer> listRoleResource(Integer roleId) {
        LambdaQueryWrapper<BasicRbacRoleResource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicRbacRoleResource::getRoleId, roleId);
        List<BasicRbacRoleResource> roleResources = basicRbacRoleResourceMapper.selectList(wrapper);
        return roleResources.stream().map(BasicRbacRoleResource::getResourceId).collect(Collectors.toList());
    }

    @Transactional
    public void saveBatchRoleResource(List<BasicRbacRoleResource> roleResources) {
        Date now = new Date();
        roleResources.forEach(r -> r.setCreatedAt(now));
        LambdaQueryWrapper<BasicRbacRoleResource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BasicRbacRoleResource::getRoleId, roleResources.get(0).getRoleId());
        basicRbacRoleResourceMapper.delete(wrapper);
        int row = basicRbacRoleResourceMapper.insertBatch(roleResources);
        Assert.isTrue(roleResources.size() == row, ErrorCode.ROLE_RESOURCE_INSERT);
    }
}
